package com.sharelords.biz.join;

import com.github.pagehelper.PageHelper;
import com.sharelords.biz.annotation.check.CHKBlank;
import com.sharelords.biz.annotation.global.QueryAllWhileParamAllSkip;
import com.sharelords.biz.annotation.param.Prop;
import com.sharelords.biz.annotation.skip.BlankSkip;
import com.sharelords.biz.annotation.skip.NullSkip;
import com.sharelords.biz.annotation.sort.Sort;
import com.sharelords.biz.annotation.value.Like;
import com.sharelords.biz.annotation.value.NotLike;
import com.sharelords.biz.enums.*;
import com.sharelords.biz.page.Page;
import com.sharelords.biz.util.CheckUtil;
import com.sharelords.biz.util.ClassUtil;
import com.sharelords.biz.util.InvokeUtil;
import com.sharelords.biz.util.InvokeUtil.Child;
import org.apache.commons.lang.StringUtils;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.text.MessageFormat;
import java.util.*;

/**
 * @author 千古龙少
 * @Description: 方法组装
 * @date 2019年9月15日 下午6:50:02
 */
public class MethodJoin<Bo> {

    /**
     * 换行
     */
    private static final String LINE_FEED = "\r\n";
    /**
     * 用于查询的方法主体
     */
    private static final String methodContent = "@Override" + LINE_FEED
            + "public {0} {1} ({2}) '{'" + LINE_FEED
            // 校验
            + " {3}"
            + "	return queryUtil.{4}(mapper,{5} new Impl() '{'" + LINE_FEED
            + "		@Override" + LINE_FEED
            + "		public Example impl(Example example, Criteria criteria) '{'" + LINE_FEED
            + "			{6}" + LINE_FEED
            + "			return example;" + LINE_FEED
            + "		'}'" + LINE_FEED
            + "	'}');" + LINE_FEED
            + "'}'" + LINE_FEED;
    /**
     * 参数校验
     */
    private static final String paramCheck = "if (CheckUtil.existBlank({0})) '{'" + LINE_FEED
            + "			return {1};" + LINE_FEED
            + "		'}'" + LINE_FEED;
    /**
     * 查询条件null校验
     */
    private static final String conditionNullCheck = "if ({0} != null) '{'" + LINE_FEED
            + "			{1}" + LINE_FEED
            + "		'}'" + LINE_FEED;
    /**
     * 查询条件空校验（null、空字符串、空集合）
     */
    private static final String conditionBlankCheck = "if (!CheckUtil.existBlank({0})) '{'" + LINE_FEED
            + "			{1}" + LINE_FEED
            + "		'}'" + LINE_FEED;
    /**
     * 查询条件
     */
    private static final String queryCondition = "criteria.{0}(\"{1}\", {2});";
    /**
     * 排序条件
     */
    private static final String sortCondition = "example.setOrderByClause(\"{0}\");";
    /**
     * 参数
     */
    private static final String paramJoin = "final {0} {1}";

    /**
     * 业务模型class
     */
    private Class<Bo> boClazz;

    /**
     * 方法列表
     */
    private List<String> methodContentList;
    /**
     * 方法中的引用
     */
    private List<Class<?>> methodImportList = new ArrayList<>();

    /**
     * 方法名
     */
    private String methodName;
    /**
     * 方法返回值类型名称
     */
    private String methodReturnTypeName;
    /**
     * 方法参数集
     */
    private StringBuilder methodParamBuilder;
    /**
     * 参数都跳过时，如果该值存在，则返回所有
     */
    private QueryAllWhileParamAllSkip queryAllWhileParamAllSkip;
    /**
     * 调用的查询方法对应枚举项
     */
    private QueryNameTypeEnum methodQueryName;
    /**
     * 分页对象参数名
     */
    private String pageParamName;
    /**
     * 需要进行非空校验的元素列表
     */
    private List<String> methodCheckElemList;
    /**
     * 实际进行查询操作的元素列表
     */
    private List<String> methodOperaElemList;
    /**
     * 返回
     */
    private String methodReturnStr;
    /**
     * 自定义对象计数
     */
    private int methodSelfDefinePojoCount = 0;

    public MethodJoin(Class<?> origClass, Class<Bo> boClass) {
        this.boClazz = boClass;
        this.methodContentList = this.groupMethodList(origClass);
    }

    /**
     * 获取引用class对象列表
     *
     * @return
     * @author 千古龙少
     * @date 2019年9月22日 下午6:55:16
     */
    public List<String> getMethods() {
        return this.methodContentList;
    }

    /**
     * 获取引用class对象列表
     *
     * @return
     * @author 千古龙少
     * @date 2019年9月22日 下午6:55:16
     */
    public List<Class<?>> getImports() {
        return this.methodImportList;
    }

    /**
     * 方法列表组装
     *
     * @param clazz 接口class
     * @return
     * @author 千古龙少
     * @date 2019年9月15日 下午6:52:34
     */
    private List<String> groupMethodList(Class<?> clazz) {
        if (clazz == null) {
            return Collections.emptyList();
        }

        this.methodImportList.add(Example.class);
        this.methodImportList.add(Criteria.class);
        this.methodImportList.add(PageHelper.class);
        this.methodImportList.add(CheckUtil.class);

        List<Method> methodList = ClassUtil.getMethodList(clazz);

        return InvokeUtil.transList(methodList, new Child<String, Method>() {
            @Override
            public String group(Method method) {
                // 初始化
                methodName = "";
                methodReturnTypeName = "";
                methodParamBuilder = new StringBuilder();
                queryAllWhileParamAllSkip = null;
                methodQueryName = QueryNameTypeEnum.queryLimitOne;
                pageParamName = "";
                methodCheckElemList = new ArrayList<>();
                methodOperaElemList = new ArrayList<>();
                methodSelfDefinePojoCount = 0;
                methodReturnStr = "null";

                // 方法组装
                return groupMethodStr(method);
            }
        });
    }

    /**
     * 方法组装
     *
     * @param method
     * @return
     * @author 千古龙少
     * @date 2019年9月15日 下午7:04:00
     */
    private String groupMethodStr(Method method) {
        if (method == null) {
            return null;
        }

        methodName = method.getName();
        if (!PrefixEnum.checkPrefix(methodName)) {
            throw new RuntimeException("方法名只能以[" + PrefixEnum.getAllPrefixStr() + "]中的一个开头，方法名为：" + this.getMethodRealPath(method));
        }

        Class<?> returnType = method.getReturnType();
        methodReturnTypeName = returnType.getSimpleName();

        if (methodName.startsWith(PrefixEnum.queryCountBy.getPrefix())) {
            // 数量查询
            if (!returnType.equals(Integer.class)) {
                throw new RuntimeException("方法名以[" + PrefixEnum.queryCountBy.getPrefix() + "]开头的，返回值请使用Integer类型，当前类型为[" + methodReturnTypeName + "]，方法名为：" + this.getMethodRealPath(method));
            }
            methodQueryName = QueryNameTypeEnum.queryCount;
        }

        if (returnType.equals(List.class)) {
            Type genericReturnType = method.getGenericReturnType();
            // 得到实际的类型参数数组
            Type[] generics = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            if (!generics[0].equals(this.boClazz)) {
                throw new RuntimeException(MessageFormat.format("不支持的list泛型类型，只能接受[{0}]，实际收到[{1}]", this.boClazz, generics[0]));
            }

            methodReturnTypeName += "<" + boClazz.getSimpleName() + ">";
            methodQueryName = QueryNameTypeEnum.queryMore;
            methodReturnStr = "Collections.emptyList()";
        } else if (!ClassUtil.ifClassIn(returnType, Integer.class, int.class, this.boClazz)) {
            throw new RuntimeException("不支持的返回值类型，returnType为：" + returnType);
        }

        Class<?>[] parameterTypes = method.getParameterTypes();
        if (parameterTypes.length == 0) {
            // 无参数，抛出异常
            throw new RuntimeException("参数不能为空，方法名为：" + this.getMethodRealPath(method));
        }

        // 注解
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();

        // 方法的参数名(采用占位名称)
        String[] parameterNames = new String[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++) {
            parameterNames[i] = "param" + i;
        }

        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> paramType = parameterTypes[i];
            Annotation[] anoArr = parameterAnnotations[i];

            String paramTypeName = paramType.getSimpleName();
            String paramName = parameterNames[i];
            String param = MessageFormat.format(paramJoin, paramTypeName, paramName);
            if (methodParamBuilder.length() > 0) {
                methodParamBuilder.append(", ");
            }
            methodParamBuilder.append(param);

            // 引用类型
            String paramTypePath = paramType.getCanonicalName();
            if (!paramTypePath.startsWith("java.lang.")) {
                this.methodImportList.add(paramType);
            }

            // 方法内容拼接
            this.groupMethodContentJoin(paramType, paramName, anoArr);
        }

        // 条件元素全局非空和非null
        List<String> globalConditionList = ConditionExtraMethodValueEnum.getMethodGlobalConditions(method);
        if (!globalConditionList.isEmpty()) {
            methodOperaElemList.addAll(globalConditionList);
        }

        // 所有标记为空时，返回所有记录的标记注解
        queryAllWhileParamAllSkip = method.getAnnotation(QueryAllWhileParamAllSkip.class);

        // 排序
        Sort sortAno = method.getAnnotation(Sort.class);
        if (sortAno != null) {
            String sortStr = MessageFormat.format(sortCondition, sortAno.value());
            methodOperaElemList.add(sortStr);
        }

        // 方法拼接
        return this.joinMethodStr();
    }

    /**
     * 方法内容拼接
     *
     * @param paramType
     * @param paramName
     * @param anoArr
     * @author 千古龙少
     * @date 2019年11月2日 下午2:02:44
     */
    private void groupMethodContentJoin(Class<?> paramType, String paramName, Annotation[] anoArr) {
        // 非自定义对象
        if (this.isSystemClass(paramType)) {
            // 条件组装
            this.groupCondition(paramName, anoArr);
            return;
        }

        // 分页对象
        if (paramType.equals(Page.class)) {
            methodQueryName = QueryNameTypeEnum.queryPage;
            pageParamName = " " + paramName + ",";
            return;
        }

        // 自定义对象
        if (methodSelfDefinePojoCount < 1) {
            // 传入对象需进行非空校验
            methodCheckElemList.add(paramName);

            Field[] fields = paramType.getDeclaredFields();
            for (Field field : fields) {
                // 过滤掉final修饰的属性
                if (Modifier.isFinal(field.getModifiers())) {
                    continue;
                }
                // 条件组装
                this.groupConditionFromField(paramName, paramType, field);
            }
        } else {
            throw new RuntimeException("最多只能有一个自定义类型参数，方法名为：" + methodName);
        }

        methodSelfDefinePojoCount++;
    }

    /**
     * 条件组装(参数)
     *
     * @param paramName
     * @param anoArr
     * @author 千古龙少
     * @date 2019年11月2日 下午6:16:21
     */
    private void groupCondition(String paramName, Annotation[] anoArr) {
        Annotation propAnno = ClassUtil.getAnnoIn(Prop.class, anoArr);
        if (propAnno == null) {
            // 无参数@Prop注解，抛出异常
            throw new RuntimeException("参数必须带有@Prop注解，方法名为：" + methodName);
        }

        // 数据模型属性名称
        String propName = ((Prop) propAnno).value();
        if (StringUtils.isBlank(propName)) {
            // 参数@Prop注解值为空，抛出异常
            throw new RuntimeException("@Prop注解value不能为空，方法名为：" + methodName);
        }

        // 条件组装
        this.groupConditionImpl(paramName, anoArr, propName);
    }

    /**
     * 条件组装(字段)
     *
     * @param paramName
     * @param paramType
     * @param field
     * @author 千古龙少
     * @date 2019年11月2日 下午7:27:08
     */
    private void groupConditionFromField(String paramName, Class<?> paramType, Field field) {
        String fieldName = field.getName();
        String fieldGetMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        try {
            paramType.getMethod(fieldGetMethodName);
        } catch (NoSuchMethodException | SecurityException e) {
            throw new RuntimeException("请为" + paramType.getSimpleName() + "的" + fieldName + "属性提供公共的get方法！");
        }

        Annotation[] anoArr = field.getAnnotations();

        String propName;
        Annotation propAnno = ClassUtil.getAnnoIn(Prop.class, anoArr);
        if (propAnno == null) {
            // 无参数@Prop注解，取字段名
            propName = fieldName;
        } else {
            propName = ((Prop) propAnno).value();
        }

        String conditionValue = paramName + "." + fieldGetMethodName + "()";

        // 条件组装
        this.groupConditionImpl(conditionValue, anoArr, propName);
    }

    /**
     * 条件组装具体实现
     *
     * @param paramName
     * @param anoArr
     * @param propName
     * @return void
     * @Author: 千古龙少
     * @Time: 2019/11/30 22:56
     */
    private void groupConditionImpl(String paramName, Annotation[] anoArr, String propName) {
        // 校验注解
        Annotation checkAnno = null;
        // 连接注解
        Annotation joinAnno = null;
        // 值比较注解
        Annotation valueAnno = null;
        // 复杂值比较注解
        Annotation valueExtraAnno = null;
        // 条件if判断注解
        Annotation skipAnno = null;
        for (Annotation paramAnno : anoArr) {
            if (checkAnno == null) {
                if (paramAnno.annotationType().isAssignableFrom(CHKBlank.class)) {
                    checkAnno = paramAnno;
                    methodCheckElemList.add(paramName);
                    continue;
                }
            }

            if (joinAnno == null) {
                if (JoinEnum.checkIfJoinAnno(paramAnno.annotationType())) {
                    joinAnno = paramAnno;
                    continue;
                }
            }

            if (valueAnno == null) {
                if (ConditionValueEnum.checkIfValueAnno(paramAnno.annotationType())) {
                    valueAnno = paramAnno;
                    continue;
                }
            }

            if (valueExtraAnno == null) {
                if (ConditionExtraValueEnum.checkIfExtraValueAnno(paramAnno.annotationType())) {
                    valueExtraAnno = paramAnno;
                    continue;
                }
            }

            if (skipAnno == null) {
                if (paramAnno.annotationType().isAssignableFrom(NullSkip.class)
                        || paramAnno.annotationType().isAssignableFrom(BlankSkip.class)) {
                    skipAnno = paramAnno;
                    continue;
                }
            }
        }

        // 条件组装
        this.groupConditions(propName, paramName, joinAnno, valueAnno, valueExtraAnno, skipAnno);
    }

    /**
     * 条件组装
     *
     * @param propName
     * @param paramName
     * @param joinAnno
     * @param valueAnno
     * @param skipAnno
     * @author 千古龙少
     * @date 2019年10月20日 下午4:55:56
     */
    private void groupConditions(String propName, String paramName, Annotation joinAnno, Annotation valueAnno, Annotation valueExtraAnno, Annotation skipAnno) {
        String joinStr = JoinEnum.getJoin(joinAnno == null ? null : joinAnno.annotationType());
        String valueJoinStr = ConditionValueEnum.getValueJoin(valueAnno == null ? null : valueAnno.annotationType());
        if (StringUtils.isBlank(joinStr) || StringUtils.isBlank(valueJoinStr)) {
            throw new RuntimeException("未找到有效的条件连接符注解！");
        }

        String valueParamName = paramName;
        if (valueAnno != null) {
            Class<? extends Annotation> valueAnoClass = valueAnno.annotationType();
            if (valueAnoClass.isAssignableFrom(Like.class)) {
                // 模糊查询
                valueParamName = this.getLeftStr(((Like) valueAnno).left()) + paramName + this.getRightStr(((Like) valueAnno).right());
            } else if (valueAnoClass.isAssignableFrom(NotLike.class)) {
                valueParamName = this.getLeftStr(((NotLike) valueAnno).left()) + paramName + this.getRightStr(((NotLike) valueAnno).right());
            }
        }

        String conditionStr;
        if (valueExtraAnno != null) {
            conditionStr = ConditionExtraValueEnum.getNewCriteriaStr(valueExtraAnno.annotationType(), propName, valueParamName, valueJoinStr, joinStr);
        } else {
            conditionStr = MessageFormat.format(queryCondition, joinStr + valueJoinStr, propName, valueParamName);
        }

        if (skipAnno != null) {
            Class<? extends Annotation> skipAnoClass = skipAnno.annotationType();
            if (skipAnoClass.isAssignableFrom(NullSkip.class)) {
                conditionStr = MessageFormat.format(conditionNullCheck, paramName, conditionStr);
            } else if (skipAnoClass.isAssignableFrom(BlankSkip.class)) {
                conditionStr = MessageFormat.format(conditionBlankCheck, paramName, conditionStr);
            }
        }

        methodOperaElemList.add(conditionStr);
    }

    /**
     * 方法拼接
     *
     * @return
     * @author 千古龙少
     * @date 2019年11月2日 下午2:11:43
     */
    private String joinMethodStr() {
        String methodParam = methodParamBuilder.toString();

        String methodCheck = "";
        if (methodCheckElemList != null && !methodCheckElemList.isEmpty()) {
            methodCheck = MessageFormat.format(paramCheck
                    , StringUtils.join(methodCheckElemList, ", ")
                    , methodReturnStr);
        }

        String methodOpera = "";
        if (methodOperaElemList != null && !methodOperaElemList.isEmpty()) {
            methodOpera = StringUtils.join(methodOperaElemList, LINE_FEED);
        }

        String methodQueryNameStr = methodQueryName.getName();
        if (queryAllWhileParamAllSkip != null) {
            methodQueryNameStr = methodQueryName.getNameButAll();
        }

        return MessageFormat.format(methodContent
                , methodReturnTypeName
                , methodName
                , methodParam
                , methodCheck
                , methodQueryNameStr
                , pageParamName
                , methodOpera);
    }

    /**
     * 是否是系统的class对象
     *
     * @param clazz
     * @return
     * @author 千古龙少
     * @date 2019年10月6日 下午4:57:52
     */
    private boolean isSystemClass(Class<?> clazz) {
        if (clazz == null) {
            throw new RuntimeException("未传入对象");
        }

        if (clazz.getClassLoader() != null) {
            // 非系统类
            return false;
        }

        if (clazz.isPrimitive()) {
            // 基本类型
            throw new RuntimeException("请使用包装类型!");
        }

        if (clazz.isArray() || Map.class.isAssignableFrom(clazz)) {
            throw new RuntimeException("参数类型有误，不适用于数组和map集合！");
        }

        return ClassUtil.ifClassExtendsIn(clazz, String.class, Number.class, Collection.class);
    }

    /**
     * 获取方法全路径
     *
     * @param method
     * @return java.lang.String
     * @Author: 千古龙少
     * @Time: 2019/11/27 0:17
     */
    private String getMethodRealPath(Method method) {
        return method.getDeclaringClass().getName() + "." + method.getName();
    }

    /**
     * 获取左侧值
     *
     * @param leftValue
     * @return java.lang.String
     * @Author: 千古龙少
     * @Time: 2019/12/1 15:52
     */
    private String getLeftStr(String leftValue) {
        if (StringUtils.isBlank(leftValue)) {
            return "";
        }

        return "\"" + leftValue + "\" + ";
    }

    /**
     * 获取右侧值
     *
     * @param rightValue
     * @return java.lang.String
     * @Author: 千古龙少
     * @Time: 2019/12/1 15:52
     */
    private String getRightStr(String rightValue) {
        if (StringUtils.isBlank(rightValue)) {
            return "";
        }

        return " + \"" + rightValue + "\"";
    }

}
